package ru.ibrae.cr.remoteclient;

import org.apache.log4j.Logger;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

import org.springframework.context.support.AbstractApplicationContext;

import ru.ibrae.cr.client.Lookup;
import ru.ibrae.cr.domain.User;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class RemoteClientInitializer {
    //~ Static fields/initializers ---------------------------------------------

    private static final Logger logger = Logger.getLogger(
            RemoteClientInitializer.class);
    private static AbstractApplicationContext context;
    private static IOsgiContextFactory osgiContextFactory;
    private static String dbUsername;
    private static String dbPassword;
    private static List<IConnectionEventListener> listeners = new ArrayList<IConnectionEventListener>();

    //~ Constructors -----------------------------------------------------------
    private RemoteClientInitializer() {
    }

    //~ Methods ----------------------------------------------------------------
    public static void start(IOsgiContextFactory osgiContextFactory,
            String dbUsername, String dbPassword) {
        RemoteClientInitializer.osgiContextFactory = osgiContextFactory;
        RemoteClientInitializer.dbUsername = dbUsername;
        RemoteClientInitializer.dbPassword = dbPassword;
        startContext();
    }

    public static void stop() {
        stopContext();
    }

    public static AbstractApplicationContext getContext() {
        return context;
    }

    public static <T> T getBean(Class<T> type, String beanName) {
        return (T) context.getBean(beanName);
    }

    @SuppressWarnings("deprecation")
    public static void testConnection()
            throws Exception {
        Lookup.getDataProvider().loadAll(User.class);
    }

    private static synchronized void startContext() {
        if (( context == null ) || !context.isRunning()) {
            boolean started = false;
            String sessionMode = System.getProperty("hibernateSessionMode");

            if (sessionMode == null) {
                System.setProperty("hibernateSessionMode", "Synchronized");
            }

            try {
                logger.info("Initializing Spring application context...");

                List<String> configs = new ArrayList<String>();

                if (osgiContextFactory != null) {
                    configs.add("classpath:/ru/ibrae/cr/remoteclient/client.xml");

                    context = osgiContextFactory.createOsgiContext(configs);
                } else {
                    configs.add("ru/ibrae/cr/remoteclient/client.xml");
                    context = new CustomClassPathXmlApplicationContext(
                            configs.toArray(new String[configs.size()]),
                            false);
                }

                configureContext();
                context.refresh();

                context.registerShutdownHook();
                started = true;

                logger.info("Spring application context initialized successfully.");
            }
            catch (Throwable t) {
                logger.error("No server connection", t);

                if (!started) {
                    context.close();
                    context = null;
                }

                throw new RuntimeException("Error creating spring context", t);
            }
        }
    }

    private static void configureContext()
            throws Exception {
        PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer();
        Properties clientProperties = Lookup.getClientProperties();

        if (dbUsername != null) {
            clientProperties.setProperty("jdbc.username", dbUsername);
        }

        if (dbPassword != null) {
            clientProperties.setProperty("jdbc.password", dbPassword);
        }

        configurer.setProperties(clientProperties);
        context.addBeanFactoryPostProcessor(configurer);
    }

    private static synchronized void stopContext() {
        if (context != null) {
            context.close();
            context = null;
        }
    }

    public static synchronized void registerListener(IConnectionEventListener listener) {
        listeners.add(listener);
    }

    public static synchronized void unregisterListener(IConnectionEventListener listener) {
        listeners.remove(listener);
    }

    public static synchronized void fireLogIn(String userName) {
        for (IConnectionEventListener listener : listeners) {
            listener.fireLogIn(userName);
        }
    }

    public static synchronized void fireLogOut() {
        for (IConnectionEventListener listener : listeners) {
            listener.fireLogOut();
        }
    }
}
